# -*- coding:UTF-8 -*-
import queue
import threading
import contextlib
import time

StopEvent = object()
RUN = 0  # 定义线程池的三种状态
CLOSE = 1
TERMINATE = 2
iNum = 0
'''
开启最大个数为5个的队列，
'''


class ThreadHelper(object):
    
    def __init__(self, max_num, max_task_num=None):
        if max_task_num:  # 如果传了最大队列数，就设置，否则就是无限大。
            self.q = queue.Queue(max_task_num)
        else:
            self.q = queue.Queue()
        self.max_num = max_num  # 设置最大线程数
        self.cancel = False  # 假如已经执行close了，就不再执行任务，生成线程处理了
        self.generate_list = []  # 已经生成的线程数列表
        self.free_list = []  # 空闲的线程数列表
        self._state = RUN
    
    def run(self, func, args, callback=None):
        """
        线程池执行一个任务
        :param func: 任务函数
        :param args: 任务函数所需参数
        :param callback: 任务执行失败或成功后执行的回调函数，回调函数有两个参数1、任务函数执行状态；2、任务函数返回值（默认为None，即：不执行回调函数）
        :return: 如果线程池已经终止，则返回True否则None
        """
        if self.cancel:  # 假如已经执行close了，就不再执行任务，生成线程处理了
            return
        if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:  # 假如空闲的线程列表为空，并且已经生成的线程数小于最大线程数
            self.generate_thread()  # 创建线程
        
        w = (func, args, callback,)  # 把当前任务放入队列，也就是run循环了300次，就有300个任务放入队列
        self.q.put(w)  # 注意：队列数是多少个，就要开启几个线程，因为当要关闭的线程池时，
        # 要把空对象加到队列。线程判断获取到是空对象（此时已经把queue里的任务都取完了）就关闭线程。
        
        global iNum
        iNum += 1
        # print('qsize:',str(self.q.qsize()))
    
    def generate_thread(self):
        """
        创建一个线程
        """
        t = threading.Thread(target=self.call)  # 执行call函数
        t.start()
    
    def call(self):
        """
        循环去获取任务函数并执行任务函数
        """
        current_thread = threading.currentThread  # 获取当前线程
        self.generate_list.append(current_thread)  # 把当前线程加入到已经生成线程列表
        
        event = self.q.get()  # 从队列里取一个任务
        while event != StopEvent:  # 假如 这个任务不是空对象
            
            func, arguments, callback = event  # 传进去的任务是个元组，由函数，参数，回调函数组成。
            try:
                result = func(*arguments)  # 执行任务，返回result
                print("Thread result" + str(result))
                success = True  # 执行成功，返回状态为True
            except Exception as e:
                success = False
                result = None
            else:
                if callback is not None:  # 假如有回调函数
                    try:
                        callback(success, result)  # 把状态和返回值传给回调函数执行
                    except Exception as e:
                        pass
            # 执行worker_state函数，空闲线程列表里是否加入个线程。在yield处执行with下的代码
            with self.worker_state(self.free_list, current_thread):
                if self._state == TERMINATE:  # 假如线程池状态是TERMINATE
                    print(11111111111111111111111)
                    event = StopEvent  # 就把当前任务赋值为空对象，while循环不满足，这样就走else的内容
                
                else:
                    event = self.q.get()  # 如果不是TERMINATE状态，则把当前任务赋给event对象
        else:  # 如果while循环不满足，或者while循环完了，没有break，就执行else内容。
            
            self.generate_list.remove(current_thread)  # 队列获取到了空对象，就关闭线程（从列表中移除当前的线程）
            print(len(self.generate_list))
    
    def close(self):  # 先执行close(),再执行join()
        """
        执行完所有的任务后，所有线程停止
        """
        if self._state == RUN:
            self._state = CLOSE
            self.cancel = True
        full_size = len(self.generate_list)  # 查看已经生成的线程数个数
        while full_size:
            self.q.put(StopEvent)  # 往队列尾部加上一个空对象，由于队列是先进先出的，所以空对象是最后获取的，通过空对象就能关闭线程。
            full_size -= 1  # 循环的次数为生成的线程的总个数
    
    def terminate(self):  # 直接执行terminate()
        """
        无论是否还有任务，终止线程
        """
        self._state = TERMINATE
        print("len:", str(len(self.generate_list)))
        while self.generate_list:  # 假如线程列表不为空，就往队列里加上空对象
            print('q.qsize():', str(self.q.qsize()))
            self.q.get()
            self.q.put(StopEvent)
        
        # self.q = queue.Queue()
        print(self.q.empty())  # 查看队列是否为空，相当于q.size==0
        print('------------' + str(self.q.qsize()))
    
    def join(self):  # CLOSE和join结合用
        """Waits until all outstanding tasks have been completed."""
        assert self._state in (CLOSE,)
        delay = 0.0005
        if self._state == CLOSE:
            while self.q.qsize() > 0:
                delay = min(delay * 2, .05)
    
    @contextlib.contextmanager  # 上下文管理器
    def worker_state(self, state_list, worker_thread):  # 传入的是空闲线程列表和当前线程
        """
        用于记录线程中正在等待的线程数
        """
        state_list.append(worker_thread)  # 把当前线程加到空闲线程里，yield前面的代码相当于执行__enter__,
        try:
            yield  # yield是执行with worker_state下的代码，
        finally:  # yield后面的代码相当于执行__exit__
            state_list.remove(worker_thread)  # 执行完一个queue的所有任务了，就移除这个线程了。因为一个队列对应着一个线程。
